﻿using ExtensionExamples;
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using FluentAssertions;
using ApiConceptsFramework;
using ApiConceptsFramework.Contract;
using System.Reflection;

namespace test.ApiConceptsFramework
{
	[TestFixture]
	public class ReflectionExtensionsTests
	{
		[Test]
		public void Extension_methods_for_nongeneric_type_are_correctly_loaded()
		{
			//Arrange
			var usings = new List<string> {"ExtensionExamples", "ExtensionExamples.Sub"};
			var assemblies = new List<Assembly> {GetType().Assembly};
			var target = new MeasurableTarget {CurrentUsings = usings, ReferencedAssemblies = assemblies, Type = typeof(ExtensionExamples.ExtendableBaseClass)};

			//Act
			var extensionMethods = target.GetExtensionMethods().ToList();

			//Assert
			extensionMethods.Should().HaveCount(2);
			extensionMethods.Select(e => e.Name).Should().BeEquivalentTo(new object[] {"ExtensionMethod1", "ExtensionMethod2"});
		}

		[Test]
		public void Extension_methods_for_nongeneric_subtype_are_correctly_loaded()
		{
			//Arrange
			var usings = new List<string> { "ExtensionExamples", "ExtensionExamples.Sub" };
			var assemblies = new List<Assembly> { GetType().Assembly };
			var target = new MeasurableTarget { CurrentUsings = usings, ReferencedAssemblies = assemblies, Type = typeof(ExtensionExamples.Sub.ExtendableSubClass) };

			//Act
			var extensionMethods = target.GetExtensionMethods().ToList();

			//Assert
			extensionMethods.Should().HaveCount(4);
			extensionMethods.Select(e => e.Name).Should().BeEquivalentTo(new object[] { "ExtensionMethod1", "ExtensionMethod2", "ExtensionMethod3", "ExtensionMethod4" });
		}

		[Test]
		public void Extension_methods_for_generic_type_are_correctly_loaded_from_SystemCore()
		{
			//Arrange
			var usings = new List<string> { "System.Linq" };
			var assemblies = new List<Assembly>(); //System.Core should automatically be taken into account
			var target = new MeasurableTarget { CurrentUsings = usings, ReferencedAssemblies = assemblies, Type = typeof(List<string>) };

			//Act
			var extensionMethods = target.GetExtensionMethods().ToList();

			//Assert
			extensionMethods.Select(m => m.Name).Should().Contain("Where").And.Contain("FirstOrDefault");
		}

		[Test]
		public void GetBaseTypes()
		{
			//Act
			var baseTypes = (typeof (StringList)).GetBaseTypes().ToList();

			//Assert
			baseTypes.Should().HaveCount(1);
			baseTypes[0].Should().Be(typeof (List<string>));
		}

		[Test]
		public void IsGenericTypeDefAndInheritedOrImplemented_interface_implementation_is_recognized()
		{
			//Arrange
			var baseType = typeof (IEnumerable<>);
			var targetType = typeof (List<string>);
			
			//Act
			var result = baseType.IsGenericTypeDefAndInheritedOrImplementedBy(targetType);
			
			//Assert
			result.Should().BeTrue();
		}

		[Test]
		public void IsGenericTypeDefAndInheritedOrImplemented_class_inheritance_is_recognized()
		{
			//Arrange
			var baseType = typeof(List<>);
			var targetType = typeof (ExtensionExamples.StringList);

			//Act
			var result = baseType.IsGenericTypeDefAndInheritedOrImplementedBy(targetType);

			//Assert
			result.Should().BeTrue();
		}

		[Test]
		public void IsGenericTypeDefAndInheritedOrImplemented_no_inheritance()
		{
			//Arrange
			var baseType = typeof(Dictionary<,>);
			var targetType = typeof(ExtensionExamples.StringList);

			//Act
			var result = baseType.IsGenericTypeDefAndInheritedOrImplementedBy(targetType);

			//Assert
			result.Should().BeFalse();
		}
	}
}

namespace ExtensionExamples
{
	public class ExtendableBaseClass { }

	public static class Extensions
	{
		public static void ExtensionMethod1(this ExtendableBaseClass x) { }
		public static void ExtensionMethod2(this ExtendableBaseClass x, string y) { }
	}

	public class StringList : List<string> { } 

}

namespace ExtensionExamples.Sub
{
	public class ExtendableSubClass : ExtendableBaseClass { }

	public static class Extensions
	{
		public static void ExtensionMethod3(this ExtendableSubClass x) { }
		public static string ExtensionMethod4(this ExtendableSubClass x, int y) { return null; }
	}
}

